package com.like.bluetooth.classic.client.connect.executor

import android.annotation.SuppressLint
import android.bluetooth.*
import android.content.Context
import android.util.Log
import com.like.bluetooth.callback.BluetoothCallback
import com.like.bluetooth.classic.client.connect.callback.ConnectCallbackManager
import com.like.bluetooth.exception.BluetoothException
import com.like.bluetooth.util.*
import kotlinx.coroutines.CoroutineScope
import java.io.IOException
import java.util.*

/**
 * 蓝牙连接及数据操作的真正逻辑
 */
@SuppressLint("MissingPermission")
internal class ConnectExecutor(context: Context, address: String?) : BaseConnectExecutor(context, address) {
    companion object {
        /*
        1、传统蓝牙采用的是SPP（Serial Port Profile）协议进行数据传输。
        2、SPP的UUID：00001101-0000-1000-8000-00805F9B34FB
        3、手机一般以客户端的角色主动连接SPP协议设备
         */
        private val connectUuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
    }

    private var mmSocket: BluetoothSocket? = null
    private val mConnectCallbackManager: ConnectCallbackManager by lazy {
        ConnectCallbackManager(address)
    }

    init {
        if (address.isNullOrEmpty() || !BluetoothAdapter.checkBluetoothAddress(address)) {
            throw BluetoothException("invalid address：$address")
        }
        mConnectCallbackManager.registerBroadcastReceiver(mContext)
    }

    override fun onConnect(
        scope: CoroutineScope, onSuccess: (() -> Unit)?, onError: ((Throwable) -> Unit)?
    ) {
        val bluetoothAdapter = mContext.getBluetoothAdapter()
        if (bluetoothAdapter == null) {
            onError?.invoke(BluetoothException("连接蓝牙失败，bluetoothAdapter is null：$address"))
            return
        }
        val device = bluetoothAdapter.getRemoteDevice(address)
        if (device == null) {
            onError?.invoke(BluetoothException("连接蓝牙失败，获取蓝牙设备失败：$address"))
            return
        }
        mConnectCallbackManager.setConnectBleCallback(object : BluetoothCallback<BluetoothDevice>() {
            override fun onSuccess(data: BluetoothDevice) {
                mConnectCallbackManager.init(scope, mmSocket)
                onSuccess?.invoke()
            }

            override fun onError(exception: BluetoothException) {
                onDisconnect()
                onError?.invoke(exception)
            }
        })
        // 不在这里调用cancelDiscovery()方法，因为它需要BLUETOOTH_SCAN权限
        // Cancel discovery because it otherwise slows down the connection.
//        bluetoothAdapter.cancelDiscovery()
        closeSocket()
        try {
            // 这里如果采用 createRfcommSocketToServiceRecord() 方法获取 socket ，会弹出配对对话框，但是这个对话框在取消后有可能就再也弹不出来了，原因未知。
            // 所以改为使用 createInsecureRfcommSocketToServiceRecord() 方法连接，它不会进行配对操作。是不安全的连接。
            mmSocket = device.createInsecureRfcommSocketToServiceRecord(connectUuid)
            // Connect to the remote device through the socket. This call blocks
            // until it succeeds or throws an exception.
            mmSocket?.connect()
        } catch (e: Exception) {
            Log.e("ConnectExecutor", "${e.message}")
        }
    }

    override fun onDisconnect() {
        closeSocket()
        mConnectCallbackManager.cancelJob()
    }

    override fun close() {
        super.close()
        mConnectCallbackManager.unregisterBroadcastReceiver(mContext)
    }

    private fun closeSocket() {
        try {
            mmSocket?.close()
            mmSocket = null
        } catch (e: Exception) {
        }
    }

    override fun onWrite(data: ByteArray, onSuccess: (() -> Unit)?, onError: ((Throwable) -> Unit)?) {
        val os = mmSocket?.outputStream
        if (os == null) {
            onError?.invoke(BluetoothException("outputStream is null"))
            return
        }
        try {
            os.write(data)
            os.flush()
        } catch (e: IOException) {
            onError?.invoke(e)
            return
        }
        onSuccess?.invoke()
    }

    override fun onSetReadCallback(onResult: (ByteArray) -> Unit) {
        mConnectCallbackManager.setReadCallback(object : BluetoothCallback<ByteArray>() {
            override fun onSuccess(data: ByteArray) {
                onResult(data)
            }
        })
    }


    override fun onRemoveReadCallback() {
        mConnectCallbackManager.setReadCallback(null)
    }

}